import 'dart:convert';
import 'dart:ffi' as ffi;
import 'dart:io';
import 'dart:isolate';
import 'package:ffi/ffi.dart';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

//UTF8兼容ASCII
typedef WordType = ffi.Pointer<ffi.Pointer<Utf8>>;

typedef GenChainNativeType = ffi.Int Function(
    WordType, ffi.Int, WordType, ffi.Char, ffi.Char, ffi.Char, ffi.Int);
typedef GenChainType = int Function(
    WordType, int, WordType, int, int, int, int);

typedef GenChainsNativeType = ffi.Int Function(WordType, ffi.Int, WordType);
typedef GenChainsType = int Function(WordType, int, WordType);

typedef AllocResultNativeType = WordType Function();
typedef AllocResultType = AllocResultNativeType;

typedef FreeResultNativeType = ffi.Int Function(WordType, ffi.Int);
typedef FreeResultType = int Function(WordType, int);

typedef ErrMsgType = Utf8;

const libraryPath = 'core.dll';
const pointerSize = 8; // 64位指针
const maxResultSize = 20000;
// ffi.DynamicLibrary dynlib;
// final GenChainType genChainWordN;
//
// AllocResultType allocResult;
// FreeResultType freeResult;

final dynlib = ffi.DynamicLibrary.open(libraryPath);
final AllocResultType allocResult = dynlib
    .lookup<ffi.NativeFunction<AllocResultNativeType>>('alloc_result')
    .asFunction();
final FreeResultType freeResult = dynlib
    .lookup<ffi.NativeFunction<FreeResultNativeType>>('free_result')
    .asFunction();
final GenChainType genChainWordN = dynlib
    .lookup<ffi.NativeFunction<GenChainNativeType>>('gen_chain_word')
    .asFunction();
final GenChainType genChainCharN = dynlib
    .lookup<ffi.NativeFunction<GenChainNativeType>>('gen_chain_char')
    .asFunction();
final GenChainsType genChainsAllN = dynlib
    .lookup<ffi.NativeFunction<GenChainsNativeType>>('gen_chains_all')
    .asFunction();

final ffi.Pointer<ErrMsgType> errMsg = dynlib.lookup<ErrMsgType>('error_msg');
final ffi.Pointer<ffi.Int> errFlag = dynlib.lookup<ffi.Int>('error_flag');

Future<List<String>> genChainsAll(List<String> words) async {
  List<String> ret = await Isolate.run(() async {
    var result = allocResult();
    var r = genChainsAllN(
      _words2Native(words),
      words.length,
      result,
    );
    if (errFlag.value > 0) {
      errFlag.value = 0;
      throw 'core return err: ${errMsg.toDartString()}';
    }
    var ret = _native2Words(result, r);
    freeResult(result, r);
    return ret;
  });
  return ret;
}

Future<List<String>> genChainWord(List<String> words, String head, String tail,
    String noBegin, bool enableLoop) async {
  var ret = await Isolate.run(() {
    var result = allocResult();
    var r = genChainWordN(
        _words2Native(words),
        words.length,
        result,
        ascii.encode(head).first,
        ascii.encode(tail).first,
        ascii.encode(noBegin).first,
        enableLoop ? 1 : 0);
    if (errFlag.value > 0) {
      errFlag.value = 0;
      throw 'core return err: ${errMsg.toDartString()}';
    }
    var ret = _native2Words(result, r);
    freeResult(result, r);
    return ret;
  });
  return ret;
}


Future<List<String>> genChainChar(List<String> words, String head, String tail,
    String noBegin, bool enableLoop) async {
  var ret = await Isolate.run(() {
    var result = allocResult();
    var wordsNative = _words2Native(words);
    var r = genChainCharN(
        wordsNative,
        words.length,
        result,
        ascii.encode(head).first,
        ascii.encode(tail).first,
        ascii.encode(noBegin).first,
        enableLoop ? 1 : 0);
    if (errFlag.value > 0) {
      errFlag.value = 0;
      throw 'core return err: ${errMsg.toDartString()}';
    }
    var ret = _native2Words(result, r);
    freeResult(result, r);
    malloc.free(wordsNative);
    return ret;
  });
  return ret;
}

WordType _words2Native(List<String> words) {
  words.removeWhere((element) => element.isEmpty);
  WordType wordsArray = malloc.allocate(words.length * pointerSize);
  for (int i = 0; i < words.length; i++) {
    var pointer2WordPointer = wordsArray.elementAt(i);
    var nativeUtf8Str = words[i].toNativeUtf8();
    if (ascii.encode(words[i]).any((element) =>
        element < 0x41 || element > 0x5a && element < 0x61 || element > 0x7a)) {
      // 如果不是ASCII能表示的或者不是字母会抛出异常
      throw '单词只能由英文字母组成';
    }
    pointer2WordPointer.value = nativeUtf8Str;
  }
  return wordsArray;
}

List<String> _native2Words(WordType result, int len) {
  List<String> dartResult = [];
  for (int i = 0; i < len; i++) {
    var s = result.elementAt(i).value.toDartString();
    dartResult.add(s);
  }
  return dartResult;
}
